package com.hunttown.common.dao.transaction;

import com.ibatis.sqlmap.client.SqlMapClient;
import com.hunttown.common.dao.base.IBaseDao;
import com.hunttown.common.dao.datasource.MasterSlaveDataSource;
import com.hunttown.common.dao.sharding.ShardingRouters;
import com.hunttown.common.dao.sharding.ShardingUtil;
import com.hunttown.common.sharding.client.datasource.ShardingDataSources;
import com.hunttown.common.sharding.client.support.RouteResult;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.Assert;

public class TransactionManagers {
    private static final Logger logger = LoggerFactory.getLogger(TransactionManagers.class);
    private DataSource defaultDataSource;
    private Map<DataSource, PlatformTransactionManager> transactionManagerMap;
    private Map<DataSource, String> dataSourceBeanNameMap;
    private Map<PlatformTransactionManager, String> transactionManagerBeanNameMap;

    public TransactionManagers() {
    }

    public void setDefaultDataSource(DataSource defaultDataSource) {
        this.defaultDataSource = defaultDataSource;
    }

    public void setTransactionManagerMap(Map<DataSource, PlatformTransactionManager> transactionManagerMap) {
        this.transactionManagerMap = transactionManagerMap;
    }

    public void setDataSourceBeanNameMap(Map<DataSource, String> dataSourceBeanNameMap) {
        this.dataSourceBeanNameMap = dataSourceBeanNameMap;
    }

    public void setTransactionManagerBeanNameMap(Map<PlatformTransactionManager, String> transactionManagerBeanNameMap) {
        this.transactionManagerBeanNameMap = transactionManagerBeanNameMap;
    }

    public List<PlatformTransactionManager> getTransactionManager(List<DataSource> dsList) {
        if (dsList != null && !dsList.isEmpty()) {
            List<PlatformTransactionManager> transactionManagerList = new ArrayList();

            PlatformTransactionManager transactionManager;
            for (Iterator i$ = dsList.iterator(); i$.hasNext(); transactionManagerList.add(transactionManager)) {
                DataSource ds = (DataSource) i$.next();
                if (ds instanceof TransactionAwareDataSourceProxy) {
                    ds = ((TransactionAwareDataSourceProxy) ds).getTargetDataSource();
                }

                if (ds instanceof MasterSlaveDataSource) {
                    ds = ((MasterSlaveDataSource) ds).selectDataSource();
                }

                transactionManager = (PlatformTransactionManager) this.transactionManagerMap.get(ds);
                if (logger.isDebugEnabled()) {
                    String dataSourceBeanName = (String) this.dataSourceBeanNameMap.get(ds);
                    String transactionManagerBeanName = (String) this.transactionManagerBeanNameMap.get(transactionManager);
                    logger.debug("transaction manger select : [{}={}]", dataSourceBeanName, transactionManagerBeanName);
                }
            }

            return transactionManagerList;
        } else {
            return Collections.EMPTY_LIST;
        }
    }

    public PlatformTransactionManager getTransactionManager(DataSource ds) {
        if (ds instanceof TransactionAwareDataSourceProxy) {
            ds = ((TransactionAwareDataSourceProxy) ds).getTargetDataSource();
        }

        if (ds instanceof MasterSlaveDataSource) {
            ds = ((MasterSlaveDataSource) ds).selectDataSource();
        }

        PlatformTransactionManager transactionManager = (PlatformTransactionManager) this.transactionManagerMap.get(ds);
        if (logger.isDebugEnabled()) {
            String dataSourceBeanName = (String) this.dataSourceBeanNameMap.get(ds);
            String transactionManagerBeanName = (String) this.transactionManagerBeanNameMap.get(transactionManager);
            logger.debug("transaction manger select : [" + dataSourceBeanName + "=" + transactionManagerBeanName + "]");
        }

        return transactionManager;
    }

    public PlatformTransactionManager getTransactionManager(SqlMapClient sqlMapClient) {
        DataSource ds = sqlMapClient.getDataSource();
        return this.getTransactionManager(ds);
    }

    public PlatformTransactionManager getDefaultTransactionManager() {
        return this.getTransactionManager(this.defaultDataSource);
    }

    public String getTransactionManagerBeanName(PlatformTransactionManager transactionManager) {
        return (String) this.transactionManagerBeanNameMap.get(transactionManager);
    }

    public String getDataSourceBeanName(DataSource dataSource) {
        return (String) this.dataSourceBeanNameMap.get(dataSource);
    }

    public <T> T executeInTrans(TransactionCallback<T> callback, IBaseDao... daoArray) {
        List<PlatformTransactionManager> transactionManagerList = new ArrayList();
        if (daoArray != null && daoArray.length > 0) {
            IBaseDao[] arr$ = daoArray;
            int len$ = daoArray.length;

            for (int i$ = 0; i$ < len$; ++i$) {
                IBaseDao dao = arr$[i$];
                transactionManagerList.add(this.getTransactionManager(dao.getDataSource()));
            }
        } else {
            transactionManagerList.add(this.getDefaultTransactionManager());
        }

        Assert.notEmpty(transactionManagerList, "none transaction manager!");
        if (logger.isDebugEnabled()) {
            Iterator i$ = transactionManagerList.iterator();

            while (i$.hasNext()) {
                PlatformTransactionManager transactionManager = (PlatformTransactionManager) i$.next();
                logger.debug("begin transaction use [" + this.getTransactionManagerBeanName(transactionManager) + "] transaction manager");
            }
        }

        return this.executeInTrans(callback, transactionManagerList, 0, transactionManagerList.size());
    }

    public <T> T executeInShardingTrans(ShardingRouters shardingRouters, TransactionCallback<T> callback, Object... shardingObjs) {
        List<PlatformTransactionManager> transactionManagerList = new ArrayList();
        ShardingDataSources shardingDataSources = ShardingDataSources.getInstance();
        Object[] arr$ = shardingObjs;
        int len$ = shardingObjs.length;

        for (int i$ = 0; i$ < len$; ++i$) {
            Object sharding = arr$[i$];
            RouteResult routeResult = ShardingUtil.doRoute(shardingRouters, sharding);
            Iterator k$ = routeResult.getResourceIdentities().iterator();

            while (k$.hasNext()) {
                String dataSourceName = (String) k$.next();
                transactionManagerList.add(this.getTransactionManager(shardingDataSources.getDataSource(dataSourceName)));
            }
        }

        Assert.notEmpty(transactionManagerList, "none transaction manager!");
        if (logger.isDebugEnabled()) {
            Iterator i$ = transactionManagerList.iterator();

            while (i$.hasNext()) {
                PlatformTransactionManager transactionManager = (PlatformTransactionManager) i$.next();
                logger.debug("begin sharding transaction use [" + this.getTransactionManagerBeanName(transactionManager) + "] transaction manager");
            }
        }

        return this.executeInTrans(callback, transactionManagerList, 0, transactionManagerList.size());
    }

    private <T> T executeInTrans(final TransactionCallback<T> callback, final List<PlatformTransactionManager> transactionManagerList, final int index, final int count) {
        return index < count - 1 ? (new TransactionTemplate((PlatformTransactionManager) transactionManagerList.get(index))).execute(new TransactionCallback<T>() {
            public T doInTransaction(TransactionStatus status) {
                return TransactionManagers.this.executeInTrans(callback, transactionManagerList, index + 1, count);
            }
        }) : (new TransactionTemplate((PlatformTransactionManager) transactionManagerList.get(index))).execute(callback);
    }
}